package com.chenfan.magic.entity;

import com.chenfan.magic.resolver.FreemarkerResolver;
import com.chenfan.magic.tags.*;
import com.chenfan.magic.utils.MapKit;
import com.chenfan.magic.utils.SpringUtil;
import com.chenfan.magic.utils.StringKit;
import com.google.common.collect.Maps;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.Map.Entry.comparingByValue;
import static java.util.stream.Collectors.toMap;

/**
 * @author lm
 */
@Data
@Slf4j
public class MQLModel implements Serializable {

    private static final long serialVersionUID = 1L;

    public static String tag = "mql";

    private String mql;

    /**
     * 一个全局有序id列表
     */
    private Map<String, Integer> sortIdMap = Maps.newHashMap();

    /**
     * validate标签组
     */
    private Map<String, TagValidate> validateModelMap = Maps.newHashMap();

    private Map<String, TagResolve> resolveModelMap = Maps.newHashMap();

    private Map<String, TagEmail> emailModelMap = Maps.newHashMap();

    private Map<String, TagSms> smsModelMap = Maps.newHashMap();

    private Map<String, TagRobot> robotModelMap = Maps.newHashMap();

    /**
     * template标签组
     */
    private Map<String, TagTemplate> templateModelMap = Maps.newHashMap();

    /**
     * script标签组
     */
    private Map<String, TagScript> scriptModelMap = Maps.newHashMap();

    /**
     * shell标签组
     */
    private Map<String, TagShell> shellModelMap = Maps.newHashMap();

    /**
     * http标签组
     */
    private Map<String, TagHttp> httpModelMap = Maps.newHashMap();

    /**
     * redis标签组
     */
    private Map<String, TagRedis> redisModelMap = Maps.newHashMap();

    public void addRobot(TagRobot robotModel) {
        this.robotModelMap.put(robotModel.getId(), robotModel);
        this.sortIdMap.put(robotModel.getId(), robotModel.getSort());
    }

    public TagRobot getRobot(String key, Map<String, Object> idcRuntimePool, Map<String, Object> paramMap, Map<String, Object> attributeMap) {
        TagRobot model = this.robotModelMap.get(key);
        Map<String, Object> modelMap = MapKit.pojoToMap(model);
        convert(idcRuntimePool, modelMap, paramMap, attributeMap);
        return MapKit.mapToPojo(modelMap, TagRobot.class);
    }

    public void addResolve(TagResolve resolveModel) {
        this.resolveModelMap.put(resolveModel.getId(), resolveModel);
        this.sortIdMap.put(resolveModel.getId(), resolveModel.getSort());
    }

    public TagResolve getResolve(String key, Map<String, Object> idcRuntimePool, Map<String, Object> paramMap, Map<String, Object> attributeMap) {
        TagResolve model = this.resolveModelMap.get(key);
        Map<String, Object> modelMap = MapKit.pojoToMap(model);
        convert(idcRuntimePool, modelMap, paramMap, attributeMap);
        return MapKit.mapToPojo(modelMap, TagResolve.class);
    }

    public void addEmail(TagEmail emailModel) {
        this.emailModelMap.put(emailModel.getId(), emailModel);
        this.sortIdMap.put(emailModel.getId(), emailModel.getSort());
    }

    public TagEmail getEmail(String key, Map<String, Object> idcRuntimePool, Map<String, Object> paramMap, Map<String, Object> attributeMap) {
        TagEmail model = this.emailModelMap.get(key);
        Map<String, Object> modelMap = MapKit.pojoToMap(model);
        convert(idcRuntimePool, modelMap, paramMap, attributeMap);
        return MapKit.mapToPojo(modelMap, TagEmail.class);
    }

    public void addSms(TagSms smsModel) {
        this.smsModelMap.put(smsModel.getId(), smsModel);
        this.sortIdMap.put(smsModel.getId(), smsModel.getSort());
    }

    public TagSms getSms(String key, Map<String, Object> idcRuntimePool, Map<String, Object> paramMap, Map<String, Object> attributeMap) {
        TagSms model = this.smsModelMap.get(key);
        Map<String, Object> modelMap = MapKit.pojoToMap(model);
        convert(idcRuntimePool, modelMap, paramMap, attributeMap);
        return MapKit.mapToPojo(modelMap, TagSms.class);
    }

    public void addValidate(TagValidate validateModel) {
        this.validateModelMap.put(validateModel.getId(), validateModel);
        this.sortIdMap.put(validateModel.getId(), validateModel.getSort());
    }

    public TagValidate getValidate(String key, Map<String, Object> idcRuntimePool, Map<String, Object> paramMap, Map<String, Object> attributeMap) {
        TagValidate model = this.validateModelMap.get(key);
        Map<String, Object> modelMap = MapKit.pojoToMap(model);
        convert(idcRuntimePool, modelMap, paramMap, attributeMap);
        return MapKit.mapToPojo(modelMap, TagValidate.class);
    }

    public void addTemplate(TagTemplate templateModel) {
        this.templateModelMap.put(templateModel.getId(), templateModel);
        this.sortIdMap.put(templateModel.getId(), templateModel.getSort());
    }

    public TagTemplate getTemplate(String key, Map<String, Object> idcRuntimePool, Map<String, Object> paramMap, Map<String, Object> attributeMap) {
        TagTemplate model = this.templateModelMap.get(key);
        Map<String, Object> modelMap = MapKit.pojoToMap(model);
        convert(idcRuntimePool, modelMap, paramMap, attributeMap);
        return MapKit.mapToPojo(modelMap, TagTemplate.class);
    }

    public void addScript(TagScript scriptModel) {
        this.scriptModelMap.put(scriptModel.getId(), scriptModel);
        this.sortIdMap.put(scriptModel.getId(), scriptModel.getSort());
    }

    public TagScript getScript(String key, Map<String, Object> idcRuntimePool, Map<String, Object> paramMap, Map<String, Object> attributeMap) {
        TagScript model = this.scriptModelMap.get(key);
        Map<String, Object> modelMap = MapKit.pojoToMap(model);
        convert(idcRuntimePool, modelMap, paramMap, attributeMap);
        return MapKit.mapToPojo(modelMap, TagScript.class);
    }

    public void addShell(TagShell commandModel) {
        this.shellModelMap.put(commandModel.getId(), commandModel);
        this.sortIdMap.put(commandModel.getId(), commandModel.getSort());
    }

    public TagShell getShell(String key, Map<String, Object> idcRuntimePool, Map<String, Object> paramMap, Map<String, Object> attributeMap) {
        TagShell model = this.shellModelMap.get(key);
        Map<String, Object> modelMap = MapKit.pojoToMap(model);
        convert(idcRuntimePool, modelMap, paramMap, attributeMap);
        return MapKit.mapToPojo(modelMap, TagShell.class);
    }

    public void addHttp(TagHttp httpModel) {
        this.httpModelMap.put(httpModel.getId(), httpModel);
        this.sortIdMap.put(httpModel.getId(), httpModel.getSort());
    }

    public TagHttp getHttp(String key, Map<String, Object> idcRuntimePool, Map<String, Object> paramMap, Map<String, Object> attributeMap) {
        TagHttp model = this.httpModelMap.get(key);
        Map<String, Object> modelMap = MapKit.pojoToMap(model);
        convert(idcRuntimePool, modelMap, paramMap, attributeMap);
        return MapKit.mapToPojo(modelMap, TagHttp.class);
    }

    public void addRedis(TagRedis redisModel) {
        this.redisModelMap.put(redisModel.getId(), redisModel);
        this.sortIdMap.put(redisModel.getId(), redisModel.getSort());
    }

    public TagRedis getRedis(String key, Map<String, Object> idcRuntimePool, Map<String, Object> paramMap, Map<String, Object> attributeMap) {
        TagRedis model = this.redisModelMap.get(key);
        Map<String, Object> modelMap = MapKit.pojoToMap(model);
        convert(idcRuntimePool, modelMap, paramMap, attributeMap);
        return MapKit.mapToPojo(modelMap, TagRedis.class);
    }

    /**
     * 转换型解析器
     *
     * @param modelMap
     * @param paramMap
     */
    public void convert(Map<String, Object> idcRuntimePool, Map<String, Object> modelMap, Map<String, Object> paramMap, Map<String, Object> attributeMap) {
        Iterator<Map.Entry<String, Object>> iterator = modelMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> kv = iterator.next();
            String key = kv.getKey();
            Object value = kv.getValue();
            if (!"context".equals(key)
                    && !"element".equals(key)) {
                if (modelMap.get("__" + key) != null && StringKit.isNotBlank(modelMap.get("__" + key) + "") && !"0".equals(modelMap.get("__" + key) + "")) {
                    value = SpringUtil.getBean(FreemarkerResolver.class).resolve(idcRuntimePool, modelMap.get("__" + key) + "", paramMap);
                    log.info("mql attribute convert: " + key + " => " + value);
                    attributeMap.put("[" + modelMap.get("tag") + "_" + modelMap.get("id") + "] " + key, value);
                    modelMap.put(key, value);
                }
            }
        }
    }

    /**
     * 排序处理
     */
    public void sort() {
        // 按值排序 升序
        this.sortIdMap = sortIdMap
                .entrySet()
                .stream()
                .sorted(comparingByValue())
                .collect(
                        toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e2,
                                LinkedHashMap::new));
        if (robotModelMap.size() > 0) {
            log.info("robot: " + robotModelMap.size());
        }
        if (resolveModelMap.size() > 0) {
            log.info("resolve: " + resolveModelMap.size());
        }
        if (emailModelMap.size() > 0) {
            log.info("email: " + emailModelMap.size());
        }
        if (smsModelMap.size() > 0) {
            log.info("sms: " + smsModelMap.size());
        }
        if (validateModelMap.size() > 0) {
            log.info("validate: " + validateModelMap.size());
        }
        if (templateModelMap.size() > 0) {
            log.info("template: " + templateModelMap.size());
        }
        if (scriptModelMap.size() > 0) {
            log.info("script: " + scriptModelMap.size());
        }
        if (shellModelMap.size() > 0) {
            log.info("shell: " + shellModelMap.size());
        }
        if (httpModelMap.size() > 0) {
            log.info("http: " + httpModelMap.size());
        }
        if (redisModelMap.size() > 0) {
            log.info("redis: " + redisModelMap.size());
        }
    }

    public List<String> parser(String tagName) {
        List<String> result = new ArrayList<String>();
        String reg = "<" + tagName + "[^>]*>([^<]*)</" + tagName + ">";
        Matcher m = Pattern.compile(reg).matcher(this.mql);
        while (m.find()) {
            String r = m.group(1);
            result.add(r);
        }
        return result;
    }

    public List<String> getAttr(String tagName, String attr) {
        List<String> result = new ArrayList<String>();
        String reg = "<" + tagName + "[^<>]*?\\s" + attr + "=['\"]?(.*?)['\"]?\\s.*?>";
        Matcher m = Pattern.compile(reg).matcher(this.mql);
        while (m.find()) {
            String r = m.group(1);
            result.add(r);
        }
        return result;
    }

}
